/*! \file MemoryMappedFile_win32.cpp
 *	\brief
 *
 *	created:	2010/11/23
 *	created:	23:11:2010   10:13
 *	filename: 	C:\src\tactics-engine\libs\io\win32\MemoryMappedFile.cpp
 *	file path:	C:\src\tactics-engine\libs\io
 *	file base: MemoryMappedFile
 *	file ext:	cpp
 *	author:		Vitalii Trubchaninov
 *				vitalii.trubchaninov@ubisoft.com
 */

#include "io/stdafx.h"
#include "io/win32/MemoryMappedFile_win32.h"

namespace tt{
namespace io{

MemoryMappedFilePS::MemoryMappedFilePS()
: m_FileMappingName()
, m_FileName()
, m_Mode(MemoryMappedFileMode_READONLY)
, m_File()
, m_FileMapping()
, m_Size(0)
, m_Data(NULL)
{

}

MemoryMappedFilePS::MemoryMappedFilePS(const std::wstring& fileMappingName)
: m_FileMappingName(fileMappingName)
, m_FileName()
, m_Mode(MemoryMappedFileMode_READONLY)
, m_File()
, m_FileMapping()
, m_Size(0)
, m_Data(NULL)
{

}

MemoryMappedFilePS::~MemoryMappedFilePS()
{
	Close();
}

bool MemoryMappedFilePS::Open( const std::wstring& fileName, MemoryMappedFileMode mode )
{
	m_FileName = fileName;
	m_Mode = mode;

	if(!m_FileMappingName.empty() && Open())
	{
		//MG_LOG_DEBUG("MemoryMappedFilePS (%s) already Created - using existing");
		return true;
	}

	if(Create())
		return true;

	return false;
}

void MemoryMappedFilePS::Close()
{
	m_Size = 0;
	if (m_Data)
	{
		UnmapViewOfFile(m_Data);
	}
	m_FileMapping.reset();
	m_File.reset();
}

//////////////////////////////////////////////////////////////////////////
bool MemoryMappedFilePS::Open()
{
	utils::Handle fileMapping( OpenFileMappingW(desireAccessMaping(), false, m_FileMappingName.c_str() ) );
	if (!fileMapping.valid())
		return false;

	size_t size = GetFileSize(m_FileName);
	if(size == 0)
		return false;

	LPVOID data = MapViewOfFile(fileMapping, FILE_MAP_READ, 0, 0, size);
	if(!data)
	{
		//MG_LOG_FATAL("MemoryMappedFilePS: MapViewOfFile failed.");
		return false;
	}

	m_FileMapping.swap(fileMapping);
	m_Size = size;
	m_Data = data;

	return true;
}

bool MemoryMappedFilePS::Create()
{
	//MG_LOG_DEBUG("MemoryMappedFilePS::Creating");

	utils::Handle file( CreateFileW(m_FileName.c_str(), desireAccessFile(), shareModeFile(), NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL) );
	if (!file)
	{
		//MG_LOG_FATAL("MemoryMappedFilePS: CreateFileW failed.");
		return false;
	}

	size_t size = GetFileSize(file);
	if(size == 0)
		return false;

	const wchar_t* fileMappingName = m_FileMappingName.empty() ? NULL : m_FileMappingName.c_str();
	utils::Handle fileMapping( CreateFileMappingW(file, NULL, protectMapping(), 0, 0, fileMappingName) );
	if (!fileMapping)
	{
		//MG_LOG_FATAL("MemoryMappedFilePS: CreateFileMappingW failed.");
		return false;
	}

	LPVOID data = MapViewOfFile(fileMapping, desireAccessMaping(), 0, 0, size);
	if(!data)
	{
		//MG_LOG_FATAL("MemoryMappedFilePS: MapViewOfFile failed.");
		return false;
	}

	m_File.swap(file);
	m_FileMapping.swap(fileMapping);
	m_Size = size;
	m_Data = data;

	//MG_LOG_DEBUG("MemoryMappedFilePS::Created");

	return true;
}

size_t MemoryMappedFilePS::GetFileSize( const utils::Handle& file )
{
	LARGE_INTEGER sizeLarge;
	if (!GetFileSizeEx(file, &sizeLarge))
	{
		//MG_LOG_FATAL("MemoryMappedFilePS: GetFileSize failed.");
		return 0;
	}

	return sizeLarge.LowPart;
}

size_t MemoryMappedFilePS::GetFileSize(const std::wstring& fileName)
{
	utils::Handle file( CreateFileW(fileName.c_str(),
		FILE_READ_ATTRIBUTES,
		FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL) );
	if (!file)
	{
		//MG_LOG_FATAL("MemoryMappedFilePS: GetFileSize failed.");
		return 0;
	}

	return GetFileSize(file);
}

DWORD MemoryMappedFilePS::desireAccessFile() const
{
	return Writeable() ?
		GENERIC_READ | GENERIC_WRITE :
		GENERIC_READ;
}

DWORD MemoryMappedFilePS::shareModeFile() const
{
	return Writeable() ?
		FILE_SHARE_READ | FILE_SHARE_WRITE :
		FILE_SHARE_READ;
}

DWORD MemoryMappedFilePS::desireAccessMaping() const
{
	return Writeable() ?
		FILE_MAP_READ | FILE_MAP_WRITE :
		FILE_MAP_READ;
}

DWORD MemoryMappedFilePS::protectMapping() const
{
	return Writeable() ? PAGE_READWRITE : PAGE_READONLY;
}

} //namespace io
} //namespace protection
